package com.fujimana.rockincat.restree.phase;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.util.Enumeration;
import java.util.Locale;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import com.fujimana.rockincat.restree.context.Context;
import com.fujimana.rockincat.restree.converter.Binder;
import com.fujimana.rockincat.restree.exception.HttpException;
import com.fujimana.rockincat.restree.init.Init;
import com.fujimana.rockincat.restree.proxy.MethodMetaData;
import com.fujimana.rockincat.restree.proxy.ResourceProxy;
import com.fujimana.rockincat.restree.util.ClassUtils;
import com.fujimana.rockincat.restree.util.IOUtils;
import com.fujimana.rockincat.restree.util.HttpUtils.HttpStatusCode;

public class BindRequestValuePhase implements Phase {
	
	@Override
	public void execute(Map<String, Object> context) {
		MethodMetaData metaData = (MethodMetaData) context.get(MethodMetaData.class.getName());
		ResourceProxy proxy = (ResourceProxy) context.get(ResourceProxy.class.getName());
		Init init = (Init) context.get(Init.class.getName());
		HttpServletRequest request = (HttpServletRequest) context.get(HttpServletRequest.class.getName());
		Context rstCtx = (Context) context.get(Context.class.getName());
		
		Class<?> type = proxy.getParamBodyType(metaData.getAnnotatin());
		String name = proxy.getParamBodyName(metaData.getAnnotatin());
		
		String contentType = (String) context.get("contentType");
		Locale locale = (Locale) context.get("locale");
		String charset = (String) context.get("charset");
		
		byte[] body = null;
		if(metaData.isBody()) {
			body = readBody(request, metaData.isBody(), charset);
		}
		
		if(type == null || name == null && body != null) {
			rstCtx.set("parameter", body);
			return;
		}
		
		if(type == null && name == null && metaData.isBody() != false) {
			return;
		}
		
		Object o = null;
		if(metaData.isBody()) {
			Binder binder = init.getRequestBinder(contentType);
			
			if (binder == null) {
				throw new HttpException(HttpStatusCode.BAD_REQUEST);
			}
			o = binder.bind(body, type, locale, charset);
				
		} else {
			o = bindQuery(request, type);
		}
		
		if(o == null) {
			throw new HttpException(HttpStatusCode.BAD_REQUEST);
		}
		
		rstCtx.set(name, o);
	}
	
	@SuppressWarnings("unchecked")
	public Object bindQuery(HttpServletRequest request, Class<?> clazz) {
		try {
			Object o = clazz.newInstance();
			
			Enumeration<String> enumeration = request.getAttributeNames();
			while(enumeration.hasMoreElements()) {
				String name = enumeration.nextElement();
				
				if(ClassUtils.isField(clazz, name)) {
					ClassUtils.setField(o, name, request.getAttribute(name));
				}
			}
			
			return o;
			
		} catch (InstantiationException e) {
			// nothing to do.
		} catch (IllegalAccessException e) {
			// nothing to do.
		}
		
		return null;
	}
	
	private byte[] readBody(HttpServletRequest request, boolean isBody, String charset) {
		byte[] ret = null;
		BufferedInputStream bis = null;
		
		if(charset == null || charset.trim().equals("")) {
			charset = System.getProperty("file.encoding");
		}
		
		try {
			if(isBody) {
				ret = new byte[request.getContentLength()];
				bis = new BufferedInputStream(request.getInputStream());
				bis.read(ret);
			} else {
				String str = request.getQueryString();	
				
				ret = str.getBytes(charset);
			}
		} catch (IOException e) {
			return null;
		} finally {
			IOUtils.close(bis);
		}
		
		return ret;
	}

	@Override
	public String getId() {
		return BindRequestValuePhase.class.getSimpleName();
	}

	@Override
	public boolean isChain() {
		return true;
	}
}
